# Copyright (C) 2018-present prototyped.cn. All rights reserved.
# Distributed under the terms and conditions of the Apache License.
# See accompanying files LICENSE.


import taksi.descriptor.predef as predef
import taksi.descriptor.lang as lang
import taksi.descriptor.strutil as strutil
import taksi.generator.genutil as genutil
import taksi.version as version


CPP_MANAGER_METHOD_TEMPLATE = """
    // Load all configurations
    static void LoadAll();

    // Clear all configurations
    static void ClearAll();

    // Read content from an asset file
    static std::string ReadFileContent(const char* filepath);
    
    // default loader
    static std::function<std::string(const char*)> reader;
"""


# C++ struct generator
class CppStructGenerator:

    # 生成class定义结构，不包含结尾的'}'符号
    def gen_cpp_struct_define(self, struct):
        content = '// %s\n' % struct['comment']
        content += 'struct %s \n{\n' % struct['name']
        fields = struct['fields']
        if struct['options'][predef.PredefParseKVMode]:
            fields = genutil.get_struct_kv_fields(struct)

        inner_class_done = False
        inner_typename = ''
        inner_var_name = ''
        inner_field_names, mapped_inner_fields = genutil.get_inner_class_mapped_fields(struct)
        if len(mapped_inner_fields) > 0:
            content += self.gen_inner_struct_define(struct)
            inner_type_class = struct["options"][predef.PredefInnerTypeClass]
            inner_var_name = struct["options"][predef.PredefInnerTypeName]
            inner_typename = 'std::vector<%s>' % inner_type_class

        vec_done = False
        vec_names, vec_name = genutil.get_vec_field_range(struct)

        max_name_len = strutil.max_field_length(fields, 'name', None)
        max_type_len = strutil.max_field_length(fields, 'original_type_name', lang.map_cpp_type)
        if len(inner_typename) > max_type_len:
            max_type_len = len(inner_typename)

        for field in fields:
            field_name = field['name']
            if field_name in inner_field_names:
                if not inner_class_done:
                    typename = strutil.pad_spaces(inner_typename, max_type_len + 1)
                    name = strutil.pad_spaces(inner_var_name, max_name_len + 8)
                    content += '    %s %s; //\n' % (typename, name)
                    inner_class_done = True

            else:
                typename = lang.map_cpp_type(field['original_type_name'])
                assert typename != "", field['original_type_name']
                typename = strutil.pad_spaces(typename, max_type_len + 1)
                if field_name not in vec_names:
                    name = lang.name_with_default_cpp_value(field, typename)
                    name = strutil.pad_spaces(name, max_name_len + 8)
                    content += '    %s %s // %s\n' % (typename, name, field['comment'])
                elif not vec_done:
                    name = '%s[%d];' % (vec_name, len(vec_names))
                    name = strutil.pad_spaces(name, max_name_len + 8)
                    content += '    %s %s // %s\n' % (typename, name, field['comment'])
                    vec_done = True

        return content

    # 内部class定义
    def gen_inner_struct_define(self, struct):
        inner_fields = genutil.get_inner_class_struct_fields(struct)
        content = ''
        class_name = struct["options"][predef.PredefInnerTypeClass]
        content += '    struct %s \n' % class_name
        content += '    {\n'
        max_name_len = strutil.max_field_length(inner_fields, 'name', None)
        max_type_len = strutil.max_field_length(inner_fields, 'original_type_name', lang.map_cpp_type)
        for field in inner_fields:
            typename = lang.map_cpp_type(field['original_type_name'])
            assert typename != "", field['original_type_name']
            typename = strutil.pad_spaces(typename, max_type_len + 1)
            name = lang.name_with_default_cpp_value(field, typename)
            name = strutil.pad_spaces(name, max_name_len + 8)
            content += '        %s %s // %s\n' % (typename, name, field['comment'])
        content += '    };\n\n'
        return content

    # 生成头文件声明
    def gen_cpp_header(self, struct, gen_declare_fn):
        content = ''
        content += self.gen_cpp_struct_define(struct)
        content += '\n'
        if callable(gen_declare_fn):
            content += gen_declare_fn(struct)
        content += '};\n\n'
        return content

    #
    def gen_header_content(self, descriptors, params, gen_declare_fn):
        h_include_headers = [
            '#include <stdint.h>',
            '#include <string>',
            '#include <vector>',
            '#include <map>',
            '#include <functional>',
            '#include "Utility/Range.h"',
        ]
        header_content = '// This file is auto-generated by taxi v%s, DO NOT EDIT!\n\n#pragma once\n\n' % version.VER_STRING
        header_content += '\n'.join(h_include_headers) + '\n\n'

        if 'pkg' in params:
            header_content += '\nnamespace %s\n{\n\n' % params['pkg']

        header_content += 'class %s\n' % strutil.config_manager_name
        header_content += '{\n'
        header_content += 'public:\n'
        header_content += CPP_MANAGER_METHOD_TEMPLATE
        header_content += '};\n\n'

        for struct in descriptors:
            print(strutil.current_time(), 'start generate', struct['source'])
            header_content += self.gen_cpp_header(struct, gen_declare_fn)

        if 'pkg' in params:
            header_content += '} // namespace %s' % params['pkg']
        return header_content
